/*
 * pgp-s2k.c
 *	  OpenPGP 字符串到密钥的函数。
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他材料提供的发行版本中。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * contrib/pgcrypto/pgp-s2k.c
 */

#include "postgres.h"

#include "pgp.h"
#include "px.h"

static int fc_calc_s2k_simple(PGP_S2K *fc_s2k, PX_MD *fc_md, const uint8 *fc_key,
				unsigned fc_key_len)
{
	unsigned	fc_md_rlen;
	uint8		fc_buf[PGP_MAX_DIGEST];
	unsigned	fc_preload;
	unsigned	fc_remain;
	uint8	   *fc_dst = fc_s2k->key;

	fc_md_rlen = px_md_result_size(fc_md);

	fc_remain = fc_s2k->key_len;
	fc_preload = 0;
	while (fc_remain > 0)
	{
		px_md_reset(fc_md);

		if (fc_preload)
		{
			memset(fc_buf, 0, fc_preload);
			px_md_update(fc_md, fc_buf, fc_preload);
		}
		fc_preload++;

		px_md_update(fc_md, fc_key, fc_key_len);
		px_md_finish(fc_md, fc_buf);

		if (fc_remain > fc_md_rlen)
		{
			memcpy(fc_dst, fc_buf, fc_md_rlen);
			fc_dst += fc_md_rlen;
			fc_remain -= fc_md_rlen;
		}
		else
		{
			memcpy(fc_dst, fc_buf, fc_remain);
			fc_remain = 0;
		}
	}
	px_memset(fc_buf, 0, sizeof(fc_buf));
	return 0;
}

static int fc_calc_s2k_salted(PGP_S2K *fc_s2k, PX_MD *fc_md, const uint8 *fc_key, unsigned fc_key_len)
{
	unsigned	fc_md_rlen;
	uint8		fc_buf[PGP_MAX_DIGEST];
	unsigned	fc_preload = 0;
	uint8	   *fc_dst;
	unsigned	fc_remain;

	fc_md_rlen = px_md_result_size(fc_md);

	fc_dst = fc_s2k->key;
	fc_remain = fc_s2k->key_len;
	while (fc_remain > 0)
	{
		px_md_reset(fc_md);

		if (fc_preload > 0)
		{
			memset(fc_buf, 0, fc_preload);
			px_md_update(fc_md, fc_buf, fc_preload);
		}
		fc_preload++;

		px_md_update(fc_md, fc_s2k->salt, PGP_S2K_SALT);
		px_md_update(fc_md, fc_key, fc_key_len);
		px_md_finish(fc_md, fc_buf);

		if (fc_remain > fc_md_rlen)
		{
			memcpy(fc_dst, fc_buf, fc_md_rlen);
			fc_remain -= fc_md_rlen;
			fc_dst += fc_md_rlen;
		}
		else
		{
			memcpy(fc_dst, fc_buf, fc_remain);
			fc_remain = 0;
		}
	}
	px_memset(fc_buf, 0, sizeof(fc_buf));
	return 0;
}

static int fc_calc_s2k_iter_salted(PGP_S2K *fc_s2k, PX_MD *fc_md, const uint8 *fc_key,
					 unsigned fc_key_len)
{
	unsigned	fc_md_rlen;
	uint8		fc_buf[PGP_MAX_DIGEST];
	uint8	   *fc_dst;
	unsigned	fc_preload = 0;
	unsigned	fc_remain,
				fc_c,
				fc_curcnt,
				fc_count;

	fc_count = s2k_decode_count(fc_s2k->iter);

	fc_md_rlen = px_md_result_size(fc_md);

	fc_remain = fc_s2k->key_len;
	fc_dst = fc_s2k->key;
	while (fc_remain > 0)
	{
		px_md_reset(fc_md);

		if (fc_preload)
		{
			memset(fc_buf, 0, fc_preload);
			px_md_update(fc_md, fc_buf, fc_preload);
		}
		fc_preload++;

		px_md_update(fc_md, fc_s2k->salt, PGP_S2K_SALT);
		px_md_update(fc_md, fc_key, fc_key_len);
		fc_curcnt = PGP_S2K_SALT + fc_key_len;

		while (fc_curcnt < fc_count)
		{
			if (fc_curcnt + PGP_S2K_SALT < fc_count)
				fc_c = PGP_S2K_SALT;
			else
				fc_c = fc_count - fc_curcnt;
			px_md_update(fc_md, fc_s2k->salt, fc_c);
			fc_curcnt += fc_c;

			if (fc_curcnt + fc_key_len < fc_count)
				fc_c = fc_key_len;
			else if (fc_curcnt < fc_count)
				fc_c = fc_count - fc_curcnt;
			else
				break;
			px_md_update(fc_md, fc_key, fc_c);
			fc_curcnt += fc_c;
		}
		px_md_finish(fc_md, fc_buf);

		if (fc_remain > fc_md_rlen)
		{
			memcpy(fc_dst, fc_buf, fc_md_rlen);
			fc_remain -= fc_md_rlen;
			fc_dst += fc_md_rlen;
		}
		else
		{
			memcpy(fc_dst, fc_buf, fc_remain);
			fc_remain = 0;
		}
	}
	px_memset(fc_buf, 0, sizeof(fc_buf));
	return 0;
}

/*
 * 决定 PGP_S2K_ISALTED 迭代次数（在 OpenPGP 一字节表示中）
 *
 * 太小：弱
 * 太大：慢
 * gpg 默认值为 96 => 65536 次迭代
 *
 * 对于我们的默认值（count=-1），我们让它浮动一点：96 + 32 => 在 65536
 * 次和 262144 次之间。
 *
 * 否则，找到提供至少指定迭代次数的最小数字。
 */
static uint8 fc_decide_s2k_iter(unsigned fc_rand_byte, int fc_count)
{
	int			fc_iter;

	if (fc_count == -1)
		return 96 + (fc_rand_byte & 0x1F);
	/* 这有点暴力，但应该足够快 */
	for (fc_iter = 0; fc_iter <= 255; fc_iter++)
		if (s2k_decode_count(fc_iter) >= fc_count)
			return fc_iter;
	return 255;
}

int pgp_s2k_fill(PGP_S2K *fc_s2k, int fc_mode, int fc_digest_algo, int fc_count)
{
	int			fc_res = 0;
	uint8		fc_tmp;

	fc_s2k->mode = fc_mode;
	fc_s2k->digest_algo = fc_digest_algo;

	switch (fc_s2k->mode)
	{
		case PGP_S2K_SIMPLE:
			break;
		case PGP_S2K_SALTED:
			if (!pg_strong_random(fc_s2k->salt, PGP_S2K_SALT))
				return PXE_NO_RANDOM;
			break;
		case PGP_S2K_ISALTED:
			if (!pg_strong_random(fc_s2k->salt, PGP_S2K_SALT))
				return PXE_NO_RANDOM;
			if (!pg_strong_random(&fc_tmp, 1))
				return PXE_NO_RANDOM;
			fc_s2k->iter = fc_decide_s2k_iter(fc_tmp, fc_count);
			break;
		default:
			fc_res = PXE_PGP_BAD_S2K_MODE;
	}
	return fc_res;
}

int pgp_s2k_read(PullFilter *fc_src, PGP_S2K *fc_s2k)
{
	int			fc_res = 0;

	GETBYTE(fc_src, fc_s2k->mode);
	GETBYTE(fc_src, fc_s2k->digest_algo);
	switch (fc_s2k->mode)
	{
		case 0:
			break;
		case 1:
			fc_res = pullf_read_fixed(fc_src, 8, fc_s2k->salt);
			break;
		case 3:
			fc_res = pullf_read_fixed(fc_src, 8, fc_s2k->salt);
			if (fc_res < 0)
				break;
			GETBYTE(fc_src, fc_s2k->iter);
			break;
		default:
			fc_res = PXE_PGP_BAD_S2K_MODE;
	}
	return fc_res;
}

int pgp_s2k_process(PGP_S2K *fc_s2k, int fc_cipher, const uint8 *fc_key, int fc_key_len)
{
	int			fc_res;
	PX_MD	   *fc_md;

	fc_s2k->key_len = pgp_get_cipher_key_size(fc_cipher);
	if (fc_s2k->key_len <= 0)
		return PXE_PGP_UNSUPPORTED_CIPHER;

	fc_res = pgp_load_digest(fc_s2k->digest_algo, &fc_md);
	if (fc_res < 0)
		return fc_res;

	switch (fc_s2k->mode)
	{
		case 0:
			fc_res = fc_calc_s2k_simple(fc_s2k, fc_md, fc_key, fc_key_len);
			break;
		case 1:
			fc_res = fc_calc_s2k_salted(fc_s2k, fc_md, fc_key, fc_key_len);
			break;
		case 3:
			fc_res = fc_calc_s2k_iter_salted(fc_s2k, fc_md, fc_key, fc_key_len);
			break;
		default:
			fc_res = PXE_PGP_BAD_S2K_MODE;
	}
	px_md_free(fc_md);
	return fc_res;
}
